using System;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;

namespace Slaks.Web.Parser
{
  
  public class HtmlAnchor
  {
    protected enum LinkType {Img, NonImg};

    protected string m_rawLink;

    protected Uri m_href;

    protected string m_text;

    protected LinkType m_linkType;

		#region static patterns
		  protected static string PatternIsHref = @".*?href\s*=.*?";
		  protected static string PatternHrefJavascript = @".*?window.open.+[""'](.*?)['""].*?;";
	    protected static string PatternHref = @".*?href+\s*=\s*[""']?([^'""\(\) >]+?)[ '""].*?>";
      protected static string PatternHrefAbnormal = @".*?href+\s*=\s*(.[^\(\)]+?)\s*>";
      protected static string PatternFrameSrc = @"\bsrc+\s*=\s*[""']?([^'"" >]+?)[ '""].*?>";
		  protected static string PatternImg = @"^\s*<img";
		  protected static string PatternAlt = @".*?alt+\s*=\s*[""']?([^'"" >]+?)[ '""].*?";
		  protected static string PatternTextExtended2 = @"^\s*(.*?)\s*<";
		  protected static string PatternTextExtended = @"<.+?>\s*(.*?)\s*</.+?>+?";
		  protected static string PatternText = @"\s*?(.*?)\s*?<";
		  protected static string PatternMailto = "mailto";
	    protected static string patternWWW = @"^\s*www.";
		#endregion

    public HtmlAnchor() {}

    /// <summary>
    /// Get or set href link from anchor
    /// </summary>
    public Uri Href
    {
      get { return m_href; }
      set { m_href = value; }
    }

    /// <summary>
    /// Text in anchor
    /// </summary>
    public string Text
    {
      get { return m_text; }
      set { m_text = value; }
    }

    /// <summary>
    /// General function for extracting link and text from anchor
    /// </summary>
    /// <param name="anchorLink"></param>
    /// <param name="restText"></param>
    /// <param name="baseUri"></param>
    /// <returns></returns>
    public static HtmlAnchor ExtractAnchor(string anchorLink,string restText,Uri baseUri)
    {
      Uri uri = ExtractLinkFromAnchor(anchorLink,baseUri);
      if (uri == null) return null;

			HtmlAnchor anchor = new HtmlAnchor();

      string textOfLink = ExtractTextOfLink(restText);
      if (textOfLink == null) return null;

			textOfLink = DocumentHtmlParser.ScanTextForSpecialEntityCharacters(textOfLink);

			anchor.Href = uri;
      anchor.Text = textOfLink.ToLower();
      return anchor;
    }

    /// <summary>
    /// Extract frame link from frameset tag
    /// </summary>
    /// <param name="frameText">The text string containing the src attribute</param>
    /// <param name="baseUri">Base url of the site (localhost for local page)</param>
    /// <returns></returns>
    public static HtmlAnchor ExtractFrame(string frameText, Uri baseUri)
    {
      Match m = Regex.Match(frameText,PatternFrameSrc);
      HtmlAnchor frame = null;
      if (m.Success)
      {
        GroupCollection gc = m.Groups;
        Group g = gc[1];
        string url = g.Value;
        url = NormalizeLink(url,baseUri);
        if (url != null) 
          url = ScanLinkForAmpersandEntity(url);
        try 
        {
          Uri hRef = new Uri(baseUri,url);
          frame = new HtmlAnchor();
          frame.Href = hRef;
        }
        catch(UriFormatException) { return null; }
        catch(Exception) { return null; }
      }

      return frame;
    }

    /// <summary>
    /// Check whether anchor contains href attribute
    /// </summary>
    /// <param name="text"></param>
    /// <returns></returns>
		public static bool IsHref(string text)
		{
			Match m = Regex.Match(text,PatternIsHref,System.Text.RegularExpressions.RegexOptions.IgnoreCase);
			return m.Success;
		}

    /// <summary>
    /// Get the link from the anchor
    /// </summary>
    /// <param name="text"></param>
    /// <param name="baseUri"></param>
    /// <returns></returns>
    protected static Uri ExtractLinkFromAnchor(string text,Uri baseUri)
    {
			Uri hRef= null;
			Match m = Regex.Match(text,PatternMailto,RegexOptions.IgnoreCase);
			if (m.Success) return null;
			m = Regex.Match(text,PatternHrefJavascript,RegexOptions.IgnoreCase);
			if (m.Success)
			{
				GroupCollection gc = m.Groups;
				Group g = gc[1];
				string url = g.Value;
				url = NormalizeLink(url,baseUri);
				if (url != null) 
					url = ScanLinkForAmpersandEntity(url);
				try 
				{
					hRef = new Uri(baseUri,url);
				}
				catch(UriFormatException) { return null; }
				catch(Exception) { return null; }
			}
			else
			{
				m = Regex.Match(text,PatternHref,RegexOptions.IgnoreCase);
        if (m.Success)
        {
          GroupCollection gc = m.Groups;
          Group g = gc[1];
          string url = g.Value;
          url = NormalizeLink(url,baseUri);
          if (url != null) 
            url = ScanLinkForAmpersandEntity(url);
          try 
          {
            hRef = new Uri(baseUri,url);
          }
          catch(UriFormatException) { return null; }
          catch(Exception) { return null; }
        }
        else
        {
          m = Regex.Match(text,PatternHrefAbnormal,RegexOptions.IgnoreCase);
          if (m.Success)
          {
            GroupCollection gc = m.Groups;
            Group g = gc[1];
            string url = g.Value;
            url = NormalizeLink(url,baseUri);
            if (url != null) 
              url = ScanLinkForAmpersandEntity(url);
            try 
            {
              hRef = new Uri(baseUri,url);
            }
            catch(UriFormatException) { return null; }
            catch(Exception) { return null; }
          }
        }

			}
      return hRef;
    }

		/// <summary>
		/// Normalizes and checks for consistensy
		/// example:
		/// 1. www.mail.ru/index.htm -> http://www.mail.ru/index.htm
		/// 
		/// </summary>
		/// <param name="link">A link to check</param>
		/// <returns>String.Empty if no changes,null if the link is not valid (example '#' after removing will be '')
		///  ohterwise the normalized link</returns>
		protected static string NormalizeLink(string link,Uri baseUri)
		{
			int index = link.IndexOf('#');
			if (index > -1) //found
			{
				link = link.Remove(index,link.Length - index);
			}
      if (link.Length == 0) return baseUri.ToString();

			Match m = Regex.Match(link,patternWWW);
			if (m.Success) 
			{
				link = String.Concat("http://"+link);
			}
			if (link == String.Empty) return null;
			return link;
		}

		/// <summary>
		/// Scan for occurrence of &amp; entity in the link and replace it with '&'
		/// </summary>
		/// <param name="link">portion of the link</param>
		/// <returns>new link</returns>
		protected static string ScanLinkForAmpersandEntity(string link)
		{
			string tempLink = Regex.Replace(link,"&amp;","&",RegexOptions.IgnoreCase);
			return Regex.Replace(tempLink,"%3F|%253F","?",RegexOptions.IgnoreCase);
		}

    protected static string ExtractTextOfLink(string text)
    {
			string extractedText = String.Empty;
			
			//if <img alt='slsls' src='http://'></a>
			Match m = Regex.Match(text,PatternImg);
			if (m.Success)
			{
				m = Regex.Match(text,PatternAlt);
				//if <img> tag contains alt attribute.
				if (m.Success)
				{
					GroupCollection gc = m.Groups;
					Group g = gc[1];
					extractedText = g.Value;
					if (extractedText == String.Empty) return null;
					return extractedText;
				}
			}
			else
			{
				return FilterHtmlTags(text);
			}
      return null;
    }

		/// <summary>
		/// Filter the string from html tags
		/// </summary>
		/// <example>
		/// <h2>Slava</h2><b>Kisilevich</b> -> Slava Kisilevich
		/// </example>
		/// <param name="str"></param>
		/// <returns></returns>
		public static string FilterHtmlTags(string str)
		{
			bool isSkip = false;
			StringBuilder stringBuffer = new StringBuilder();
			for(int i=0;i<str.Length;i++)
			{
				if (str[i] == '<')
				{
					isSkip = true;
					continue;
				}
				else if (isSkip && str[i] != '>') continue;
				else if (isSkip && str[i] == '>')
				{
					isSkip = false; 
					if (stringBuffer.Length != 0) stringBuffer.Append(" "); 
					continue;
				}
				else stringBuffer.Append(str[i]);
			}

			return stringBuffer.ToString();
		}

		
  }
}
